package main

import (
	"flag"
	"io"
	"log"
	"net"

	"golang.org/x/net/context"

	"google.golang.org/grpc"
	"google.golang.org/grpc/reflection"
)

const (
	host   = "localhost:50051"
	listen = ":50051"
)

func client() {
	conn, err := grpc.Dial(host, grpc.WithInsecure())
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()

	c := NewGreetingClient(conn)
	{
		req := &SayRequest{Message: "Astone"}
		log.Println("Send:", req)
		r, err := c.Say(context.Background(), req)
		if err != nil {
			log.Fatal(err)
		}
		log.Println("Recv:", r)
	}
	{
		req := &SayRequest{Message: "Astone"}
		log.Println("Send:", req)
		r, err := c.Say(context.Background(), req)
		if err != nil {
			log.Fatal(err)
		}
		log.Println("Recv:", r)
	}

	{
		stream, err := c.SayCStream(context.Background())
		if err != nil {
			log.Fatal(err)
		}

		req1 := &SayRequest{Message: "Nice to meet you!"}
		req2 := &SayRequest{Message: "I'm Astone."}
		stream.Send(req1)
		log.Println("Send:", req1)
		stream.Send(req2)
		log.Println("Send:", req2)
		r, err2 := stream.CloseAndRecv()
		if err2 != nil {
			log.Fatal(err2)
		}
		log.Println("Recv:", r)
	}

	{
		req := &SayRequest{Message: "Astone"}
		log.Println("Send:", req)
		stream, err := c.SaySStream(context.Background(), req)
		if err != nil {
			log.Fatal(err)
		}

		for {
			r, err := stream.Recv()
			if err == io.EOF {
				break
			} else if err != nil {
				log.Fatal(err)
			}
			log.Println("Recv:", r)
		}
	}
}

type rpcServer struct{}

func (s *rpcServer) Say(ctx context.Context, in *SayRequest) (*SayReply, error) {
	log.Println("Recv:", in)
	rsp := &SayReply{Message: "You are " + in.Message + "."}
	log.Println("Send:", rsp)
	return rsp, nil
}

// C-Stream
func (s *rpcServer) SayCStream(stream Greeting_SayCStreamServer) error {
	for {
		req, err := stream.Recv()
		if err == io.EOF {
			rsp := &SayReply{Message: "Nice to meet you too!"}
			log.Println("Send:", rsp)
			return stream.SendAndClose(rsp)
		} else if err != nil {
			return err
		}
		log.Println("Recv:", req)
	}
	return nil
}

// S-Stream
func (s *rpcServer) SaySStream(in *SayRequest, stream Greeting_SaySStreamServer) error {
	log.Println("Recv:", in)

	rsp1 := &SayReply{Message: "Hi, " + in.Message + "."}
	log.Println("Send:", rsp1)
	stream.Send(rsp1)

	rsp2 := &SayReply{Message: "Nice to meet you!"}
	log.Println("Send:", rsp2)
	stream.Send(rsp2)

	return nil
}

// B-Stream
func (s *rpcServer) SayBStream(stream Greeting_SayBStreamServer) error {
	return nil
}

func interceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
	log.Println("handle start")
	defer log.Println("handle complete")

	return handler(ctx, req)
}

func server() {
	lis, err := net.Listen("tcp", listen)
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}

	s := grpc.NewServer(grpc.UnaryInterceptor(interceptor))
	RegisterGreetingServer(s, &rpcServer{})
	reflection.Register(s)
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

var (
	flag_clt bool
	flag_svr bool
)

func init() {
	flag.BoolVar(&flag_clt, "client", true, "run as client")
	flag.BoolVar(&flag_svr, "server", false, "run as server")
}

func main() {
	log.SetFlags(log.Lshortfile | log.LstdFlags)
	flag.Parse()

	if flag_svr {
		server()
		return
	}

	if flag_clt {
		client()
		return
	}
}
